// Copyright (C) 2018 - 2019, Yongming Li <l@pear.hk>

#include <sys/time.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <openssl/ripemd.h>
#include <openssl/ec.h>
#include <openssl/ecdsa.h>
#include <openssl/obj_mac.h>
#include <openssl/sha.h>
#include <stdbool.h>

#include "pear_keystore.h"


char vchPubKey[1024]  = {0x00};
char vchPrivKey[1024] = {0x00};


int static inline ECDSA_SIG_recover_key_GFp(EC_KEY *eckey, ECDSA_SIG *ecsig, const unsigned char *msg, int msglen, int recid, int check)
{
    if (!eckey) return 0;

    int ret = 0;
    BN_CTX *ctx = NULL;

    BIGNUM *x = NULL;
    BIGNUM *e = NULL;
    BIGNUM *order = NULL;
    BIGNUM *sor = NULL;
    BIGNUM *eor = NULL;
    BIGNUM *field = NULL;
    EC_POINT *R = NULL;
    EC_POINT *O = NULL;
    EC_POINT *Q = NULL;
    BIGNUM *rr = NULL;
    BIGNUM *zero = NULL;
    int n = 0;
    int i = recid / 2;

    const EC_GROUP *group = EC_KEY_get0_group(eckey);
    if ((ctx = BN_CTX_new()) == NULL) { ret = -1; goto err; }
    BN_CTX_start(ctx);
    order = BN_CTX_get(ctx);
    if (!EC_GROUP_get_order(group, order, ctx)) { ret = -2; goto err; }
    x = BN_CTX_get(ctx);
    if (!BN_copy(x, order)) { ret=-1; goto err; }
    if (!BN_mul_word(x, i)) { ret=-1; goto err; }
    if (!BN_add(x, x, ecsig->r)) { ret=-1; goto err; }
    field = BN_CTX_get(ctx);
    if (!EC_GROUP_get_curve_GFp(group, field, NULL, NULL, ctx)) { ret=-2; goto err; }
    if (BN_cmp(x, field) >= 0) { ret=0; goto err; }
    if ((R = EC_POINT_new(group)) == NULL) { ret = -2; goto err; }
    if (!EC_POINT_set_compressed_coordinates_GFp(group, R, x, recid % 2, ctx)) { ret=0; goto err; }
    if (check)
    {
        if ((O = EC_POINT_new(group)) == NULL) { ret = -2; goto err; }
        if (!EC_POINT_mul(group, O, NULL, R, order, ctx)) { ret=-2; goto err; }
        if (!EC_POINT_is_at_infinity(group, O)) { ret = 0; goto err; }
    }
    if ((Q = EC_POINT_new(group)) == NULL) { ret = -2; goto err; }
    n = EC_GROUP_get_degree(group);
    e = BN_CTX_get(ctx);
    if (!BN_bin2bn(msg, msglen, e)) { ret=-1; goto err; }
    if (8*msglen > n) BN_rshift(e, e, 8-(n & 7));
    zero = BN_CTX_get(ctx);
    if (!BN_zero(zero)) { ret=-1; goto err; }
    if (!BN_mod_sub(e, zero, e, order, ctx)) { ret=-1; goto err; }
    rr = BN_CTX_get(ctx);
    if (!BN_mod_inverse(rr, ecsig->r, order, ctx)) { ret=-1; goto err; }
    sor = BN_CTX_get(ctx);
    if (!BN_mod_mul(sor, ecsig->s, rr, order, ctx)) { ret=-1; goto err; }
    eor = BN_CTX_get(ctx);
    if (!BN_mod_mul(eor, e, rr, order, ctx)) { ret=-1; goto err; }
    if (!EC_POINT_mul(group, Q, eor, R, sor, ctx)) { ret=-2; goto err; }
    if (!EC_KEY_set_public_key(eckey, Q)) { ret=-2; goto err; }

    ret = 1;

err:
    if (ctx) {
        BN_CTX_end(ctx);
        BN_CTX_free(ctx);
    }
    if (R != NULL) EC_POINT_free(R);
    if (O != NULL) EC_POINT_free(O);
    if (Q != NULL) EC_POINT_free(Q);
    return ret;
}


// create a compact signature (65 bytes), which allows reconstructing the used public key
// hashsize : 32 (sha256)
bool pear_signCompact(char * hash, int hashsize, char * vchSig ,EC_KEY* pkey)
{
	bool fOk = false;
	ECDSA_SIG *sig = ECDSA_do_sign(hash,hashsize, pkey);
        printf("pear_signCompact %p\r\n",sig);
	if (sig==NULL)
	    return false;

        //memset(pkey,0x00,65);
	int nBitsR = BN_num_bits(sig->r);
	int nBitsS = BN_num_bits(sig->s);
        printf("nBitsR: %d and nBitsS: %d! \r\n",nBitsR,nBitsS);
	if (nBitsR <= 256 && nBitsS <= 256)
	{
	    vchSig[0] = 0+27;
	    BN_bn2bin(sig->r,&vchSig[33-(nBitsR+7)/8]);
	    BN_bn2bin(sig->s,&vchSig[65-(nBitsS+7)/8]);
	    fOk = true;
	}
	ECDSA_SIG_free(sig);
	return fOk;
}

// reconstruct public key from a compact signature
bool pear_SetCompactSignature(char* hash, int hashsize, const char* vchSig,int sigsize,EC_KEY* pkey)
{
        if (sigsize != 65)
            return false;
        if (vchSig[0]<27 || vchSig[0]>=31)
            return false;
        ECDSA_SIG *sig = ECDSA_SIG_new();
        BN_bin2bn(&vchSig[1],32,sig->r);
        BN_bin2bn(&vchSig[33],32,sig->s);

        EC_KEY_free(pkey);
        pkey = EC_KEY_new_by_curve_name(NID_secp256k1);
        if (ECDSA_SIG_recover_key_GFp(pkey, sig, hash,hashsize, vchSig[0] - 27 & ~4, 0) != 1)
        {
            ECDSA_SIG_free(sig);
            return false;
        }
        if ()
        return false;
}

int  pear_sign(char * hash , char * vchSig , EC_KEY* pkey)
{
	unsigned char pchSig[10000];
	unsigned int nSize = 0;
	if (!ECDSA_sign(0, hash, 32, pchSig, &nSize, pkey))
	    return false;
	memcpy(&vchSig[0], pchSig, nSize);
        printf("dump Signature nSize is %d\r\n",nSize);
        pear_hex_dump(vchSig,nSize);
	return nSize;
}

bool pear_verify(char * hash , char * vchSig , int size, EC_KEY* pkey)
{
	// -1 = error, 0 = bad sig, 1 = good
	if (ECDSA_verify(0, hash, 32, &vchSig[0],size, pkey) != 1)
	    return false;
	return true;
}

// echo -n a79c367bb63deafed305247814936e4de52ec44c | wc -c   20*8=160
// SHA256 : 32 BYTE
#if 1
void Hash160(const char * vch, int size,EC_KEY* pkey)
{
    unsigned char  hash1[1024] ={0x00};
    SHA256(&vch[0], size, (unsigned char*)&hash1);
    unsigned char  hash2[1024] ={0x00};

    unsigned char  hashSig[1024] ={0x00};
    printf("dump SHA256 \r\n");
    

    int sigSize = pear_sign(hash1 , hashSig,pkey);
    pear_hex_dump(hash1,256);
    printf("sigSize %d\r\n",sigSize);
    //hashSig[3]=0x00;
    if(pear_verify(hash1,hashSig,sigSize,pkey))
    {
        printf("Verify successful\r\n");
    }

    //SHA256(&vch[0], 256, (unsigned char*)&hash1);
    //printf("dump SHA256 \r\n");
    //pear_hex_dump(hash1,256);

    RIPEMD160((unsigned char*)&hash1, 32, (unsigned char*)&hash2);
    //printf("dump Hash160 \r\n");
    //pear_hex_dump(hash2,160);
    return ;
}
#endif

bool pear_SetPrivKey(EC_KEY* pkey,const char * vchPrivKey , int keysize)
{
        const unsigned char* pbegin = &vchPrivKey[0];
        if (!d2i_ECPrivateKey(&pkey, &pbegin, keysize))
            return false;
        return true;
}

void pear_GetPrivKey(EC_KEY* pkey, pr_privKey_t *privKey) 
{
    unsigned int nSize  = 0;

    nSize = i2d_ECPrivateKey(pkey, NULL);
    if (!nSize)
    {
        printf("CKey::GetPrivKey() : i2d_ECPrivateKey failed\r\n");
    }

    if (i2d_ECPrivateKey(pkey, &privKey) != nSize)
    {
        printf("PrivKey size is %d and content is %s \r\n",nSize,vchPrivKey);
    }

    printf("Dump private key\r\n");
    pear_hex_dump(vchPrivKey,nSize);
    pear_set_keystore(vchPrivKey,nSize);

    char temp[1024]={0x00};
	pear_get_keystore(temp,nSize);
	printf("dump  key from key.dat file\r\n");
	pear_hex_dump(temp,nSize);

        return ;
}

void pear_GetPubKey(EC_KEY* pkey) 
{
    unsigned int   nSize  = 0;
	unsigned char *pbegin = NULL;

    nSize = i2o_ECPublicKey(pkey, NULL);

    printf("PubKey size is %d  \r\n",nSize);

	if (!nSize)
    {
	    printf("CKey::GetPubKey() : i2o_ECPublicKey failed\r\n");
    }

    pbegin = vchPubKey;

    if (i2o_ECPublicKey(pkey, &pbegin) != nSize)
    {
        printf("CKey::GetPubKey() : i2o_ECPublicKey returned unexpected size\r\n");
    }

    printf("Dump public key\r\n");

    pear_hex_dump(vchPubKey, nSize);
    Hash160( vchPubKey, nSize, pkey);

	return ;
}

EC_KEY * pear_MakeNewKey()
{
    EC_KEY* pkey = EC_KEY_new_by_curve_name(NID_secp256k1);
    if (pkey == NULL)
            printf("EC_KEY_new_by_curve_name failed\r\n");
    if (!EC_KEY_generate_key(pkey))
        printf("Generate key error \r\n");
    return pkey;
}

void pear_FreeKey(EC_KEY* pkey)
{
   EC_KEY_free(pkey);
   return;
}

